package cn.funnymap.lgis.tools.service.metadata.model.geoscene.impl;

import cn.funnymap.lgis.tools.service.metadata.ServiceField;
import cn.funnymap.lgis.tools.service.metadata.factory.ServiceMetadataFactory;
import cn.funnymap.lgis.tools.service.metadata.model.geoscene.AbstractGeoSceneService;
import cn.funnymap.lgis.tools.util.DateUtil;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author jiao xn
 */
@Component
public class GeoSceneImageService extends AbstractGeoSceneService {
    @Override
    public void afterPropertiesSet() {
        ServiceMetadataFactory.register("ImageServer", this);
    }

    @Override
    protected void queryMetadataFromRest() {
        JSONObject serviceRestQueryResult = restTemplateUtil.getForObject(this.restQueryUrl);

        if (serviceRestQueryResult == null) {
            return;
        }

        this.serviceMetadata.setSpatialReference(
                this.getSpatialReferenceWkidFromRestQueryResult(serviceRestQueryResult));
        this.serviceMetadata.setExtent(
                this.getExtentAsWktFromRestQueryResult(serviceRestQueryResult));
        this.serviceMetadata.setFields(
                this.getFieldListFromRestQueryResult(serviceRestQueryResult));
    }
    private int getSpatialReferenceWkidFromRestQueryResult(JSONObject restQueryResult) {
        if (restQueryResult.containsKey("spatialReference")) {
            JSONObject spatialReference = restQueryResult.getJSONObject("spatialReference");
            if (spatialReference != null && spatialReference.containsKey("wkid")) {
                return spatialReference.getInteger("wkid");
            }
        }

        return -1;
    }
    private String getExtentAsWktFromRestQueryResult(JSONObject restQueryResult) {
        // 首先从fullExtent中获取服务范围
        JSONObject extent = this.getFullExtentFromRestQueryResult(restQueryResult);

        if (extent.isEmpty()) {
            // 如果fullExtent不是有效的服务范围，则从initialExtent中获取服务范围
            extent = this.getInitialExtentFromRestQueryResult(restQueryResult);

            if (extent.isEmpty()) {
                // 如果initialExtent也不是有效的服务范围，则使用默认的服务范围
                extent = JSONObject.from(DEFAULT_EXTENT);
            }
        }

        Double xmin = extent.getDouble("xmin");
        Double xmax = extent.getDouble("xmax");
        Double ymin = extent.getDouble("ymin");
        Double ymax = extent.getDouble("ymax");

        return this.convertToWkt(xmin, xmax, ymin, ymax);
    }
    private JSONObject getFullExtentFromRestQueryResult(JSONObject restQueryResult) {
        if (restQueryResult.containsKey("fullExtent")) {
            JSONObject fullExtent = restQueryResult.getJSONObject("fullExtent");

            if (this.isEffectiveExtent(fullExtent)) {
                return fullExtent;
            }
        }

        return new JSONObject();
    }
    private JSONObject getInitialExtentFromRestQueryResult(JSONObject restQueryResult) {
        if (restQueryResult.containsKey("initialExtent")) {
            JSONObject initialExtent = restQueryResult.getJSONObject("initialExtent");

            if (this.isEffectiveExtent(initialExtent)) {
                return initialExtent;
            }
        }

        return new JSONObject();
    }
    private boolean isEffectiveExtent(JSONObject extent) {
        return extent.containsKey("ymin") && extent.containsKey("xmin") && extent.containsKey("ymax") && extent.containsKey("xmax") &&
                !extent.getDouble("xmin").isNaN() && !extent.getDouble("xmax").isNaN() && !extent.getDouble("ymin").isNaN() &&
                !extent.getDouble("ymax").isNaN();
    }
    private String convertToWkt(Double xmin, Double xmax, Double ymin, Double ymax) {
        return String.format("POLYGON ((%f %f, %f %f, %f %f, %f %f, %f %f))", xmin, ymax, xmax, ymax, xmax, ymin, xmin, ymin, xmin, ymax);
    }
    private List<ServiceField> getFieldListFromRestQueryResult(JSONObject restQueryResult) {
        JSONArray serviceLayers = this.getMultipleLayersFromRestQueryResult(restQueryResult);
        if (serviceLayers.isEmpty()) {
            return new ArrayList<>();
        }

        JSONArray fields = new JSONArray();
        serviceLayers.forEach(serviceLayer -> {
            Integer subLayerId = ((JSONObject) serviceLayer).getInteger("id");
            String subLayerJsonUrl =
                    String.format("%s/%s?f=json", this.url, subLayerId);
            JSONObject subLayerJsonInfo = restTemplateUtil.getForObject(subLayerJsonUrl);

            if (subLayerJsonInfo != null && subLayerJsonInfo.containsKey("fields") && subLayerJsonInfo.getJSONArray("fields") != null) {
                fields.addAll(subLayerJsonInfo.getJSONArray("fields"));
            }
        });

        return this.convertFieldsFromJsonArray(fields);
    }
    private JSONArray getMultipleLayersFromRestQueryResult(JSONObject restQueryResult) {
        if (restQueryResult.containsKey("layers")) {
            JSONArray layers = restQueryResult.getJSONArray("layers");

            if (layers != null) {
                return layers;
            }
        }

        return new JSONArray();
    }

    @Override
    protected void queryMetadataFromItemInfo() {
        JSONObject serviceIteminfoQueryResult = restTemplateUtil.getForObject(this.itemInfoQueryUrl);

        if (serviceIteminfoQueryResult == null) {
            return;
        }

        String name = serviceIteminfoQueryResult.getString("title") != null ?
                serviceIteminfoQueryResult.getString("title") :
                serviceIteminfoQueryResult.getString("name");
        String description = serviceIteminfoQueryResult.getString("description") != null ?
                serviceIteminfoQueryResult.getString("description") :
                serviceIteminfoQueryResult.getString("summary");

        this.serviceMetadata.setName(name);
        this.serviceMetadata.setDescription(description);
        this.serviceMetadata.setType("Image Service");
    }

    @Override
    protected void queryMetadataFromMetadata() throws DocumentException {
        Document xmlDocument = restTemplateUtil.getForXml(this.metadataQueryUrl);

        if (xmlDocument == null) {
            return;
        }

        String releaseDate = this.getCreateDateFromMetadataQueryResult(xmlDocument);
        String thumbnailAsBase64 = this.getThumbnailBinaryFromMetadataQueryResult(xmlDocument);

        this.serviceMetadata.setReleaseDate(releaseDate);
        this.serviceMetadata.setThumbnail(thumbnailAsBase64);
    }
    private String getCreateDateFromMetadataQueryResult(Document xmlDocument) {
        String createDate;

        if (xmlDocument.getRootElement().element("Esri") == null) {
            createDate = DateUtil.date2str(new Date(), "yyyyMMdd");
        } else {
            createDate =
                    xmlDocument.getRootElement().element("Esri").element("CreaDate").getData().toString();
        }

        return createDate;
    }
    private String getThumbnailBinaryFromMetadataQueryResult(Document xmlDocument) {
        try {
            return xmlDocument.getRootElement().element("Binary").element("Thumbnail").element("Data").getData().toString();
        } catch (NullPointerException ignoredException) {
            return null;
        }
    }
}
